package com.wjk.kylin.id.server.exception;

import com.alibaba.fastjson2.JSON;
import com.wjk.kylin.id.server.dto.BaseOutDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author wangjkui
 * @description 全局的的异常拦截器（拦截所有的控制器）（带有@RequestMapping注解的方法上都会拦截）
 * @date 2022/8/28
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 拦截未知的异常
     */
    @ExceptionHandler(Exception.class)
    public BaseOutDTO handleException(Exception e) {
        log.error("服务器未知异常:{}", e.getMessage(), e);
        return BaseOutDTO.error(e.getMessage());
    }

    /**
     * 拦截未知的运行时异常
     */
    @ExceptionHandler(RuntimeException.class)
    public BaseOutDTO handleRuntimeException(RuntimeException e) {
        log.error("服务器未知运行时异常:{}", e.getMessage(), e);
        return BaseOutDTO.error(e.getMessage());
    }

    /**
     * 拦截业务异常
     */
    @ExceptionHandler(BaseException.class)
    public BaseOutDTO handleBusinessException(BaseException e) {
        log.error("基础业务异常 e:{}", JSON.toJSONString(e.getError()));
        return BaseOutDTO.error(e.getError());
    }

    /**
     * 拦截业务异常
     */
    @ExceptionHandler(BusinessException.class)
    public BaseOutDTO handleBusinessException(BusinessException e) {
        log.error("自定义业务异常 e:{}", JSON.toJSONString(e.getError()));
        return BaseOutDTO.error(e.getError());
    }

    /**
     * Handle constraint violation exception map.
     * 缺少请求参数的验证 RequestParam required = true 是没有传必要的参数
     *
     * @param e the e
     * @return the map
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public BaseOutDTO handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
        log.error("缺少请求参数的验证:{}", e.getMessage());
        return BaseOutDTO.error(BaseError.PARAM_ERR);
    }

    /**
     * Handle constraint violation exception map.
     * 请求为分开的验证参数
     *
     * @param e the e
     * @return the map
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public BaseOutDTO handleConstraintViolationException(ConstraintViolationException e) {
        log.error("请求为分开的验证参数:{}", e.getMessage());
        List<String> message = e.getConstraintViolations().stream().limit(1).map(ConstraintViolation::getMessage).collect(Collectors.toList());
        log.error("请求为分开的验证参数:{}", JSON.toJSONString(message));
        return BaseOutDTO.error(BaseError.PARAM_ERR.getCode(),String.join(",",message));
    }

    /**
     * 请求封为对象的验证参数
     *
     * @param e the e
     * @return the map
     */
    @ExceptionHandler(BindException.class)
    public BaseOutDTO handleBindException(BindException e) {
        log.error("请求封为对象的验证参数:{}", e.getMessage());
        String message = e.getAllErrors().get(0).getDefaultMessage();
        log.error("请求封为对象的验证参数:{}", message);
        return BaseOutDTO.error(BaseError.PARAM_ERR.getCode(),message);
    }

    /**
     * 请求封为对象的验证参数
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public BaseOutDTO handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        log.error("请求封为对象的验证参数:{}", e.getMessage());
        String message = Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage();
        log.error("请求封为对象的验证参数:{}", message);
        return BaseOutDTO.error(BaseError.PARAM_ERR.getCode(),message);
    }

}
